/**
 * Module dependencies
 */
var consts = require('../../consts/consts');
var pomelo = require('pomelo');
var utils = require('../../util/utils');
var bjl = require('./bjl_round');
var PlayerManager = require('./playerManager');

var async = require('async');
var wt = require('async').waterfall;

var logger = require('pomelo-logger').getLogger('room', pomelo.app.serverId).info;
var bankerLog = require('pomelo-logger').getLogger('banker', pomelo.app.serverId).info;
var playerLogger = require('pomelo-logger').getLogger('player', pomelo.app.serverId).info;

var client = pomelo.app.get('redisUser');
var mysql = require('../../dao/userDao');

var log = console.log;

function Room(roomId, gameId, name) {
    log("create room:" + gameId + " " + roomId + " " + name);
    logger('log', 'game: ' + gameId + " room:" + roomId + " roomName:" + name);
    var self = this;

    self.config = null;
    self.resetConfig = 0;  // 间隔多少局更新设置
    // self.loadedConfig = 0;  // 是否已经载入设置

    self.freeTime = 5; // 空闲时长
    self.xiaZhuTime = 15; // 下注时长
    self.kaiPaiTime = 10; // 开牌时长
    self.checkTime = 0; // 倒计时
    // self.checkType = consts.BaiTimeOutType.timeout_type_unknown;
    self.gameStatus = consts.BaiGameStatus.game_status_unknown;
    self.hInterval = null; // 定时器

    self.tempBidData = {};  // 临时存储一个倒计时广播内的下注情况
    self.bidCount = {  // 下注累加池
        "2": 0, // 下在庄家的
        "0": 0, // 下在闲家的
        "-1": 0 // 下在其它地方的
    };

    self.playerManager = new PlayerManager(self, consts.ROOM.MAX_MEMBER_NUM);

    self.cardPool = [];   // 牌池

    self.curCards = null;
    self.curCardsStr = '';
    self.retStr = '';
    self.curResultOdds = {};

    self.curBanker = 0;  // 记录当前庄家的 playerId
    self.curBankerRound = 0;  // 记录当前庄家参与局数，坐庄满20局就下庄
    self.curBankerScore = 0;  // 记录庄的分数，也就是赚的钱数
    self.applyBankerList = [];  // 申请上庄玩家列表
    self.bankerOnLine = true;  // 记录庄家是否掉线了

    self.cutInBanker = 0;

    self.offlinePlayer = [];  // 掉线的玩家列表

    // room channel, push msg within the room
    self.channel = null;
    self.roomId = roomId;
    self.gameId = gameId;
    self.name = name;
    self.channelService = pomelo.app.get('channelService');
    self.createChannel();
    self.initGame();
}

// 创建房间后初始化
Room.prototype.initGame = function () {
    var self = this;

    // self.SetGameStatus(consts.BaiGameStatus.game_status_free);
    self.SetTimeOut(consts.BaiGameStatus.game_status_free, 1);
    self.debug('game begin ================================================================change gameStatus free: ', self.checkTime);
    if (!self.hInterval) {
        /// 第三个参数作为self.OnTimer这个函数的参数传入
        self.hInterval = setInterval(self.OnTimer, 1000, self);
    }
};

Room.prototype.OnTimer = function (self) {
    // ptr = point = this;
    // var self = ptr;

    if (self.checkGameStatus('free')) {
        // 空闲时间
        self.ProcessCheckFree();
    }
    else if (self.checkGameStatus('xiazhu')) {
        // 下注时间
        self.ProcessCheckXiaZhu();
    }
    else if (self.checkGameStatus('kaipai')) {
        // 开牌时间
        self.ProcessCheckKaiPai();
    }

    // 每一秒都检查机器人
    self.checkRobot();

    log(self.getGameStatus());
};

Room.prototype.ProcessCheckFree = function () {
    var self = this;

    // 改为只有剩余时间大于0时候才检测是否执行开始游戏
    if (self.checkTime > 0) {
        // 倒计时，广播时间
        self.checkTime--;

        // 改为 === 0, 双重避免多次开始        
        if (self.checkTime === 0) {
            // 空闲倒计时结束，开始下注
            // self.SetGameStatus(consts.BaiGameStatus.game_status_xiazhu);
            self.SetTimeOut(consts.BaiGameStatus.game_status_xiazhu, self.xiaZhuTime);

            var ret = self.getGameStatus();

            self.SetGameStart(function () {
                // 游戏开始之后才可以下注
                self.channel.pushMessage('onGameXiaZhu', ret, null);
                self.debug('====================================change gameStatus xiaZhu: ', ret);
            });
        }
    }
    else {
        self.checkTime--;
        console.warn('空闲时间溢出了：', self.checkTime);
    }

};

Room.prototype.ProcessCheckXiaZhu = function () {
    var self = this;

    self.ProcessXiaZhu();

    if (self.checkTime > 0) {
        self.checkTime--;
        
        if (self.checkTime === 0) {  // 转为开牌  // 把<=0改为==0，避免产生结算两次的可能
            // 只用来计算
            log('self.getGameStatus()     >>>>>>>>>>>>>>');
            log(self.getGameStatus());
            // TODO: 检查其它游戏中是否没有对 SetGameEnd 做 cb
            self.SetGameEnd(function () {
                // 这样就可以确定只在checkTime = 0 的时候执行一次
                log('in setGameEnd cb! >>>>>>>>>>>>>>>>>>');
                log('结算及广播完成');

                // 结算完成之后才转为开牌状态，否则就不转
                // self.SetGameStatus(consts.BaiGameStatus.game_status_kaipai);
                self.SetTimeOut(consts.BaiGameStatus.game_status_kaipai, self.kaiPaiTime);
                var ret = self.getGameStatus();

                self.channel.pushMessage('onGameKaiPai', ret, null);
                self.debug('======================================change gameStatus kaiPai: ', ret);
            });
        }
    }
    else {
        self.checkTime--;
        
        console.warn('下注时间溢出了：', self.checkTime);
    }
};

// 开牌需要结算
Room.prototype.ProcessCheckKaiPai = function () {
    var self = this;

    self.checkTime--;

    if (self.checkTime <= 0) {
        // self.playerManager.bidData = {};  // 转到下一个状态之前，还原下注容器
        // 广播结算信息之后就清空,防止出现下一轮仍旧下注的情况
        // self.SetGameStatus(consts.BaiGameStatus.game_status_free);
        self.SetTimeOut(consts.BaiGameStatus.game_status_free, self.freeTime);

        var ret = self.getGameStatus();

        self.channel.pushMessage('onGameFree', ret, null);
        self.debug('================================================================change gameStatus free: ', ret);
    }
};

// 开始游戏
// 为了添加机器人，这里就要知道游戏结果
Room.prototype.SetGameStart = function (cb) {
    var self = this;

    // 游戏开始后，玩家开始下注
    // bjl.initCardPool(self.roomId);

    // 初始化游戏信息
    self.bidCount = {
        "2": 0, // 下在庄家的
        "0": 0, // 下在闲家的
        "-1": 0 // 下在其它地方的
    };

    self.robotBidCount = {
        "0": 0,  // 下在闲家的
        "2": 0  // 下在庄家的
    };

    var ret = self.getGameStatus();

    self.channel.pushMessage('onGameStart', ret, null);

    self.resetConfig--;

    self.setConstConfig();

    // 游戏开始之前读取redis配置
    self.loadGameConfig(function (err) {
        if (err) {
            log(err);
            throw new Error(err);
        }
        self.getCardResult(cb);
    });
};

/**
 * 从redis中读取游戏配置
 */
Room.prototype.loadGameConfig = function (cb) {
    log('Room 读取redis游戏配置');

    var self = this;
    var gameId = self.gameId;

    if (self.resetConfig >= 0) {
        log('未到重新设置时间');
        cb();
        return;
    }

    var key = 'gameConfig' + self.gameId;
    var config, redisCache = true;

    wt([
        function (wcb) {
            log('redis 查询配置');
            client.get(key, function (err, rel) {
                if (err) {
                    return wcb(err);
                }

                if (rel) {
                    config = JSON.parse(rel);
                }
                else {
                    redisCache = false;
                }

                wcb(null);
            });
        },
        function (wcb) {
            log('mysql 查询配置');

            if (redisCache) {
                return wcb(null);
            }

            // TODO:  如果只改 mysql 但是不改 redis 的话，不会生效
            var sql = 'select id, configs from game_info where id = ?';
            var args = [gameId];

            mysql.query(sql, args, function (err, rows) {
                if (err) {
                    return wcb(err);
                }

                if (rows[0].configs) {
                    config = JSON.parse(rows[0].configs);
                }
                else {
                    config = { "id": gameId };
                }
                wcb(null);
            });
        },
        function (wcb) {
            log('redis 写入配置');
            if (redisCache) {
                return wcb(null);
            }
            client.set(key, JSON.stringify(config), function (err, rel) {
                if (err) {
                    wcb(err);
                }
                else {
                    wcb(null);
                }
            });
        }
    ],
        function (err, rel) {
            log('读取配置完成', err, rel, config);
            if (!err) {
                self.config = config;
                self.resetRoomConfig();
                return cb(null);
            }
            else {
                return cb(err);
            }
        });
};


// 从配置文件中读取配置
Room.prototype.setConstConfig = function () {
    log('Room 从配置文件中读取配置');
    var self = this;

    self.minBankerCoin = consts.ROOM.MIN_BANKER_COIN * 100;
    self.sysBankerCoin = consts.ROOM.SYSTEM_BANKER_COIN * 100;
    self.maxBankerRound = consts.ROOM.MAX_BANKER_ROUND;

    var playerManager = this.playerManager;

    var robotSet = {
        minCoin: consts.ROBOT.MIN_COIN,
        maxCoin: consts.ROBOT.MAX_COIN,
        minJetton: consts.ROBOT.MIN_JETTON,
        maxJetton: consts.ROBOT.MAX_JETTON
    };

    playerManager.robotSet = robotSet;
};


 // 重新设置房间配置
Room.prototype.resetRoomConfig = function () {
    log('设置房间配置');
    var self = this;
    var playerManager = this.playerManager;

    var robotSet = playerManager.robotSet, robot;

    if (self.config) {
        self.freeTime = self.config.FREE_TIME || self.freeTime;
        self.xiaZhuTime = self.config.XIAZHU_TIME || self.xiaZhuTime;
        self.kaiPaiTime = self.config.KAIPAI_TIME || self.kaiPaiTime;

        self.resetConfig = parseInt(self.config.RESET_CONFIG) || self.resetConfig;

        // 最小上庄金额        
        if (!isNaN(self.config.MIN_BANKER_COIN)) {
            self.minBankerCoin = self.config.MIN_BANKER_COIN * 100;
        }

        // 系统可赔金钱        
        if (!isNaN(self.config.SYSTEM_BANKER_COIN)) {
            self.sysBankerCoin = self.config.SYSTEM_BANKER_COIN * 100;
        }

        // 最大上庄局数      
        if (!isNaN(self.config.MAX_BANKER_ROUND)) {
            self.maxBankerRound = self.config.MAX_BANKER_ROUND;
        }

        if (self.config.ROBOT) {
            robot = self.config.ROBOT;

            self.setRobotNum = robot.NUM || 0;

            // 必须最小金钱小于最大金钱才行
            if (robot.MIN_COIN <= robot.MAX_COIN) {
                if (robot.MIN_COIN) {
                    robotSet.minCoin = robot.MIN_COIN * 100;
                }
                if (robot.MAX_COIN) {
                    robotSet.maxCoin = robot.MAX_COIN * 100;
                }
            }

            // 必须最小下注位置小于等于最大下注位置
            if (robot.MIN_JETTON <= robot.MAX_JETTON) {
                if (robot.MIN_JETTON) {
                    robotSet.minJetton = robot.MIN_JETTON * 100;
                }
                if (robot.MAX_JETTON) {
                    robotSet.maxJetton = robot.MAX_JETTON * 100;
                }
            }

        }
    }
    // log(robotSet);
};

// 定时通知前端，其他人的下注情况
Room.prototype.ProcessXiaZhu = function () {
    log('Room.ProcessXiaZhu caught >>>>>>>>>>>>>>>>>>>>>>>');
    var self = this;

    // 机器人下注
    this.playerManager.robotBid();  // 机器人暂时不下注
    // log(self.tempBidData);
    // log(self.playerManager.bidData);

    var ret = self.tempBidData;
    var count = 0;

    for (let k in ret) {
        // if (ret.hasOwnProperty(k)) {
        //     count++;
        // }
        // log(k);
        for (let v in ret[k]) {
            // log(v);
            if (ret[k].hasOwnProperty(v)) {
                count++;
            }
            if (count) {
                break;
            }
        }
        if (count) {
            break;
        }
    }

    if (count) {
        self.channel.pushMessage('onGameBid', ret, null);
    }
    // self.channel.pushMessage('onGameBid', ret, null);

    self.tempBidData = {};  // 通知之后就清空临时变量  可能会有漏掉的情况(恰好在push开始之后，清空之前有 client 发送了 bid)

};

// 下注时间截止
Room.prototype.SetGameEnd = function (cb) {
    log('before gameEnd callback! >>>>>>>>>>>>>>>>');
    var self = this;
    var playerManager = this.playerManager;

    // 存在庄家，增加庄家局数
    if (this.existsBanker()) {
        this.curBankerRound++;
    }

    // 开牌前检查结果，判断是否切换牌型
    // 后台设置是否控制结果
    if (this.config.CONTROL) {
        // 暂时不控制
        log('控制牌局已经开启！');
        if (!playerManager.checkBidCount()) {
            playerManager.changeCard();
        }
    }
    else {
        log('控制牌局没有开启！');
    }
    // playerManager.changeCard();

    var ret = {
        cards: self.curCards,
        result: self.retStr
    };

    self.channel.pushMessage('onGameOver', ret, null);

    self.CalcResult(self.curResultOdds, cb);

    playerManager.updateRobotRound();
};

// 结算, 广播结算信息
// 广播此时的用户列表
Room.prototype.CalcResult = function (resultOdds, cb) {
    var self = this;

    log('Room.CalcResult caught >>>>>>>>>>>>>>>>>');

    log('总的下注记录');
    log(this.bidCount);
    log(this.robotBidCount);

    // log(resultOdds);
    // log(this.playerManager.bidData);
    self.playerManager.calcResultOnly(resultOdds, function (err, ret) {
        if (!err) {

            // log('广播结算结果 ret: ', ret);

            self.channel.pushMessage('onCalcResult', ret, null);

            // 只要广播了消息，就立即清空下注记录
            // 更新用户游戏局数的时候也更新了coin
            self.playerManager.bidData = {};

            // 检查庄的局数是否已经超过
            self.checkBanker();

            // 检查是否有掉线玩家
            self.checkOfflinePlayer();
            cb(null);
        }
        else {
            log(err);
            cb(err);
        }
    });
};

// 创建channel
Room.prototype.createChannel = function () {
    if (this.channel) {
        return this.channel;
    }
    var channelName = 'room_' + this.gameId + '_' + this.roomId;

    this.channel = this.channelService.getChannel(channelName, true);
    // return this.channel || null;
    if (this.channel) {
        return this.channel;
    }
    return null;
};

// 把用户添加到channel
// uid: playerId; sid: serverId;
Room.prototype.addPlayer2Channel = function (data) {
    log('Room.addPlayer2Channel caught >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>');
    if (!this.channel) {
        return false;
    }

    if (data) {
        var uid = data.playerId;
        var sid = data.serverId || data.sid;

        // 用户绑定在服务器上，sid是前端服务器 id， 在这里是 'connector-server-1'
        this.channel.add(uid, sid);
        // 广播用户上线
        var ret = this.playerManager.getPlayerData(data.playerId);

        ret.playerId = data.playerId;
        this.channel.pushMessage('onPlayerEnter', ret, null);
        playerLogger('enter', ret);

        return true;
    }
    return false;
};

// 把用户从channel剔除
// uid: playerId; sid: serverId;
Room.prototype.kickPlayer2Channel = function (data) {

    log('Room.kickPlayer2Channel >>>>>>>>>>>>>>>>>>>>');

    var self = this;

    if (!this.channel) {
        return false;
    }

    if (data) {
        self.channel.leave(data.playerId, data.serverId);
        // 广播用户下线
        self.channel.pushMessage('onPlayerLeave', { playerId: data.playerId }, null);
        playerLogger('leave', { playerId: data.playerId });
        return true;
    }
    return false;
};

Room.prototype.getPlayerNum = function () {
    return this.playerManager.getPlayerNum();
};

/**
 * 房间添加用户
 * in: data: {serverId, playerId, token, gameId, playerName, isRobot, ip}
 */
Room.prototype.addPlayer = function (data, cb) {

    var self = this;

    if (!data || typeof data !== 'object') {
        cb('data type error!', false);
        return;
    }

    this.playerManager.addPlayer(data, function (err) {

        if (err) {
            log('room addPlayer err: ', err);
            cb(err);
            return;
        }

        if (!self.playerManager.isPlayerInRoom(data.playerId)) {
            cb('have not add player:' + data.playerId + 'into room ' + this.roomId);
            return;
        }

        // 加入 channel并且广播
        if (!self.addPlayer2Channel(data)) {
            cb('can not add player' + data.playerId + 'into channel ');
            return;
        }

        cb(null);
    });

};

Room.prototype.setOwnerId = function (ownerId) {
    this.ownerId = ownerId;
};

Room.prototype.isRoomFull = function () {
    return this.playerManager.isRoomFull();
};

// push msg to all of the room members
Room.prototype.pushChatMsg2All = function (msg) {
    if (!this.channel) {
        return false;
    }
    var playerId = msg.playerId;
    var playerName = this.playerManager.getPlayerNameById(playerId);

    if (!playerName) {
        return false;
    }
    var content = playerName + ': ' + msg.text;

    this.channel.pushMessage('onChat', content, null);
    return true;
};

Room.prototype.clearAllTimers = function () {
    var self = this;

    if (!self.hInterval) {   // ?
        clearTimeout(self.hInterval);
    }
};

// 重置定时器倒计时数量和倒计时类型
Room.prototype.SetTimeOut = function (status, time) {
    var self = this;

    self.gameStatus = status;
    self.checkTime = time;
};

// 获取游戏状态
Room.prototype.getGameStatus = function () {
    var self = this;
    var retDict = {
        gameStatus: self.gameStatus,
        cbTimeLeave: self.checkTime
    };

    return retDict;
};

// 获取用户列表
Room.prototype.getPlayerList = function () {
    return this.playerManager.getPlayerList();
};

// 获取玩家状态
Room.prototype.getPlayerData = function (playerId) {
    return this.playerManager.getPlayerData(playerId);
};

// // 重置游戏状态
// Room.prototype.SetGameStatus = function (status) {
//     var self = this;

//     self.gameStatus = status;
// };

// // 取随机数
// Room.prototype.GetRandomNum = function (Min, Max) {
//     var range = Max - Min;

//     return Min + Math.round(Math.random() * range);
// };

// 查看配置，获取牌的设置
Room.prototype.getCardResult = function (cb) {
    log('getCardResult.............');

    var self = this;

    var key = 'gameCards' + this.gameId;

    client.lrange(key, 0, -1, function (err, rel) {
        if (err) {
            log(err);
            log('redis错误！');
            // return false;
        }
        else {
            if (rel.length >= 4) {
                log('换牌');
                log(rel);
                bjl.replaceCards(rel);
            }
            else {
                log('清空换的牌');
                bjl.replaceCards([]);
            }
        }
        var result = bjl.next(self.roomId);

        self.RunCardRound(result, cb);
    });
};


// 运行牌局，记录牌局结果，记录本局赔率
Room.prototype.RunCardRound = function (result, cb) {
    var self = this;

    log('Room.RunCardRound caught  >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>');

    log(result);

    var rel = result.result;
    var round = result.round;
    var points = result.points;

    var cardPa = [];
    var cardBa = [];
    var suit = {
        A: '0',
        B: '1',
        C: '2',
        D: '3'
    };

    for (var i = 1; i <= 3; i++) {
        if (round.player[i]) {
            cardPa.push(round.player[i]);
        }
        if (round.banker[i]) {
            cardBa.push(round.banker[i]);
        }
    }
    // cards = [cardP, cardB];
    // log('cards: ', cards);
    self.curCards = [cardPa.join(','), cardBa.join(',')];
    self.curRecCards = [cardPa.join(','), cardBa.join(',')].join('_');

    // 获取结果字符串
    var retStr = '';

    // 赢家
    if (rel.winner === 'banker') {
        retStr += '1';
    }
    else if (rel.winner === 'player') {
        retStr += '2';
    }
    else {
        retStr += '0';
    }

    // 对子
    if (!rel.bankerPair && !rel.playerPair) {
        retStr += '0';
    }
    else if (rel.bankerPair && !rel.playerPair) {
        retStr += '1';
    }
    else if (!rel.bankerPair && rel.playerPair) {
        retStr += '2';
    }
    else {
        retStr += '3';
    }

    // 天王 // 0:都不天王，1:庄天王，2:闲天王，3:都天王
    var bNature = rel.bNature;
    var pNature = rel.pNature;

    if (!bNature && !pNature) {
        retStr += '0';
    }
    else if (bNature && !pNature) {
        retStr += '1';
    }
    else if (!bNature && pNature) {
        retStr += '2';
    }
    else {
        retStr += '3';
    }

    // 0: 小， 1： 大
    if (rel.type === 0) {
        retStr += '0';
    }
    else {
        retStr += '1';
    }

    self.retStr = retStr;

    // log('retStr：', retStr);
    var pointStr = '闲' + points.player + '点,庄' + points.banker + '点,';

    self.curCardsStr = pointStr + utils.roundResultWords(result);

    // log('本局结果：' + self.curCardsStr);

    // 获取赔率结果
    // 0:闲对子 12； 1：闲 2； 2： 闲天王 3；
    // 3：平 9；4：同点平：33；
    // 5：庄对子 12； 6：庄 2； 7： 庄天王 3
    var resultOdds = {};

    self.curResultOdds = resultOdds;
    var winner = parseInt(retStr.substring(0, 1)),  /// 0：庄赢；1：闲赢；2：平局
        pair = parseInt(retStr.substring(1, 2));  /// 0：无对子；1：庄家对；2：闲家对；3：都是对

    // 如果平局，则押庄闲的都退回去
    // 0闲 1和 2庄 3大 4闲天王 5闲对子 6庄对子 7庄天王 8小

    var BJL = consts.ODDS.BJL;

    resultOdds[0] = (winner === 2) ? BJL.PLAYER_WIN : BJL.LOSE;
    resultOdds[1] = (winner === 0) ? BJL.DRAW : BJL.LOSE;
    resultOdds[2] = (winner === 1) ? BJL.BANKER_WIN : BJL.LOSE;
    resultOdds[3] = (rel.type === 1) ? BJL.BIG : BJL.LOSE;
    resultOdds[4] = (pNature) ? BJL.PLAYER_NATURE : BJL.LOSE;
    resultOdds[5] = (rel.playerPair) ? BJL.PLAYER_PAIR : BJL.LOSE;
    resultOdds[6] = (rel.bankerPair) ? BJL.BANKER_PAIR : BJL.LOSE;
    resultOdds[7] = (bNature) ? BJL.BANKER_NATURE : BJL.LOSE;
    resultOdds[8] = (rel.type === 0) ? BJL.SMALL : BJL.LOSE;

    if (winner === 0) {
        // 如果平局，则押庄闲的都退回去
        resultOdds[0] = BJL.DRAW_PLAYER;
        resultOdds[2] = BJL.DRAW_BANKER;
    }

    cb();  // 返回到 setGameStart

};

// 下注，这里是在相应 client的 notify，不需要广播，广播只是在每个下注倒计时时候返回
// 需要个临时变量来存储倒计时内的下注情况
// 用户只要下注，就算参加了一局。可能下注多次，因此在结算之后增加用户的游戏局数。
// msg = { '0': 5 };
Room.prototype.bid = function (playerId, msg) {
    log('Room.bid caught >>>>>>>>>>>>>>>>>>>>>>');
    var self = this;
    var bidData = this.playerManager.bidData;
    var tempBidData = this.tempBidData;

    // 检查是否是下注时间
    if (!this.checkGameStatus('xiazhu')) {
        return false;
    }

    // 禁止庄家下注
    var curBanker = this.curBanker;

    if (playerId === curBanker) {
        return false;
    }

    // 检查下注的钱庄家是否够赔
    if (!this.checkBid(msg, playerId)) {
        return false;
    }

    // 1.判断是否已经有下注信息； 2.判断是否是加注
    if (!bidData[playerId]) {
        bidData[playerId] = {};
    }
    if (!tempBidData[playerId]) {
        tempBidData[playerId] = {};
    }

    // //判断是否可以下注
    // var coinSum = 0;
    // // 这次下的注
    // for (k in msg) {
    //     coinSum += parseInt(msg[k], 10);
    // }
    // // 加上已经下的注
    // if (bidData[playerId]) {
    //     for (k in bidData[playerId]) {
    //         coinSum += parseInt(bidData[playerId][k]);
    //     }
    // }
    // // 判断下的注是否小于拥有的coin
    // if (this.playerManager.playerData[playerId].coin < coinSum) {
    //     return false;
    // }

    // 1.记录下注信息
    // 2.扣除玩家金钱
    for (var k in msg) {
        if (!bidData[playerId][k]) {
            bidData[playerId][k] = 0;
        }
        bidData[playerId][k] += msg[k];
        self.playerManager.playerData[playerId].coin -= msg[k];
    }

    // 记录临时下注信息
    // onGameBid: {"112":{"0":5,"1":5}}  广播信息
    // 改为
    // tempBidData = {
    //     '112': {  // 玩家id
    //         0: {  // 位置0
    //             5: 1, // 筹码5下了1个
    //             10: 2 // 筹码10下了2个
    //         },
    //         1: { // 位置1
    //             5: 1 // 筹码5下了1个
    //         },
    //         ...
    //     },
    //     ...
    // }
    // msg = { '0': 5 };  // 位置0下了筹码5
    var jetton = 0;  // jetton 筹码

    for (k in msg) {
        jetton = msg[k];
        if (!tempBidData[playerId][k]) {
            tempBidData[playerId][k] = {};
            tempBidData[playerId][k][jetton] = 0;
        }
        tempBidData[playerId][k][jetton]++;
    }

    log('下注成功！');
    return true;
};

// 1.检查玩家的钱是不是够
// 2.检查庄家的钱是不是够
Room.prototype.checkBid = function (msg, playerId) {
    log('Room.checkBid 检查下注是否可行 ................', JSON.stringify(msg));

    // msg = { '0': 5 };  // 客户端发过来的单位还是元
    var players = this.playerManager.playerData;

    var pCoin = players[playerId].coin;
    var coinSum = 0;

    for (var k in msg) {
        coinSum += msg[k];
    }

    // log(pCoin, coinSum);

    if (pCoin < coinSum) {
        log('玩家金钱不够');
        return false;
    }


    // 计算庄是否够赔的
    var bidCount = this.bidCount;  // 记录总下注需要赔的数量
    var bankerId = this.curBanker;
    var bCoin = this.sysBankerCoin || 10000000;  // 系统当庄最多赔10000000(十万)

    if (bankerId > 0) {
        bCoin = players[bankerId].coin;
    }

    // log('可赔金额： ', bCoin);

    var BJL = consts.ODDS.BJL;
    // 0闲 1和 2庄 3大 4闲天王 5闲对子 6庄对子 7庄天王 8小
    var bOdds = {
        0: BJL.PLAYER_WIN,
        1: BJL.DRAW,
        2: BJL.BANKER_WIN,
        3: BJL.BIG,
        4: BJL.PLAYER_NATURE,
        5: BJL.PLAYER_PAIR,
        6: BJL.BANKER_PAIR,
        7: BJL.BANKER_NATURE,
        8: BJL.SMALL
    };


    // 判断庄家是否输得起
    // 1.累加下注*赔率
    for (k in msg) {
        if (parseInt(k) === 2 || parseInt(k) === 0) {
            bidCount[k] += (msg[k] * bOdds[k]);
        }
        else {
            bidCount[-1] += (msg[k] * bOdds[k]);
        }
    }
    // 2.判断是否已经超过庄家的钱
    var allCount = bidCount[-1] + Math.abs(bidCount[2] - bidCount[0]);

    // log(bidCount);
    // log('庄的钱数：', bCoin, '最多需要赔：', allCount);

    if (bCoin < allCount) {
        // 如果下注失败，还要从记录里面扣掉的
        for (k in msg) {
            if (parseInt(k) === 2 || parseInt(k) === 0) {
                bidCount[k] -= (msg[k] * bOdds[k]);
            }
            else {
                bidCount[-1] -= (msg[k] * bOdds[k]);
            }
        }
        log(bidCount);
        log('庄家不够赔偿，返回');

        return false;
    }
    return true;
};

// 玩家下线或者掉线
Room.prototype.kickPlayer = function (data) {
    log('Room.kickPlayer caught >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>');
    // log(data);

    // 不管如何，只要掉线，就要踢出频道
    this.kickPlayer2Channel(data);  // 要从channel中踢掉，但是暂时不能从内存删掉

    // 判断是否在上庄列表，如果在的话就去掉
    var index = this.applyBankerList.indexOf(data.playerId);

    if (index > -1) {
        this.applyBankerList.splice(index, 1);
        var cancelBanker = { bankerId: data.playerId };

        this.channel.pushMessage('onCancelBanker', cancelBanker, null);

        bankerLog('cancel', cancelBanker);
    }

    // 判断是否是庄家
    if (this.curBanker === data.playerId) {
        // log('庄家掉线了');
        if (this.checkGameStatus('xiazhu')) {
            log('在下注时间');
            // 庄家下注时候掉线，要等到这一局结算之后才能踢掉
            this.bankerOnLine = false;  // 庄在线标识设为false
            this.offlinePlayer.push(this.curBanker);
            
            return true;  // 不再进行下面的？ 不能直接踢出房间，要等到结算之后才可以
        }
        else {
            // log('在非下注时间');
            // 庄家非下注时间掉线，直接换庄
            this.setBanker();
            return true;
        }
    }

    // 判断数据格式
    // if (!data || typeof data !== 'object') {
    //     return consts.ROOM.LEAVE_ROOM_RET_CODE.SYS_ERROR;
    // }

    // 判断是否在这个房间里
    if (!this.playerManager.isPlayerInRoom(data.playerId)) {
        return consts.ROOM.LEAVE_ROOM_RET_CODE.NOT_IN_ROOM;
    }

    // 判断是否成功踢出内存
    if (!this.playerManager.kickPlayer(data)) {
        return consts.ROOM.SYS_ERROR;
    }

    // // 不管如何，只要掉线，就要踢出频道
    // // 判断是否成功踢出channel
    // if (!this.kickPlayer2Channel(data)) {
    //     return consts.ROOM.SYS_ERROR;
    // }

    // 判断是否还在房间并且不在下线玩家列表
    if (this.playerManager.isPlayerInRoom(data.playerId) && (this.offlinePlayer.indexOf(data.playerId) <= -1)) {
        return consts.ROOM.SYS_ERROR;
    }

    log('room.js 成功踢出用户: ' + data.playerId);

    return consts.ROOM.LEAVE_ROOM_RET_CODE.OK;
};

// 申请上庄
// 如果只有一个人，立即上庄
Room.prototype.applyBanker = function (msg, cb) {
    var playerId = msg.playerId;
    var ret = null;
    var applyBanker = {};

    // 如果已经下注，就不能上庄了
    if (this.playerManager.bidData[playerId]) {
        return cb('player: ' + playerId + ' have already bid, can not apply banker! wait next round!');
    }

    if (this.playerManager.checkApplyBanker(playerId)) {  // 检查是否有资格上庄
        this.applyBankerList.push(playerId);

        // 广播上庄成功消息
        applyBanker = { bankerId: playerId };
        this.channel.pushMessage('onApplyBanker', applyBanker, null);

        bankerLog('apply', applyBanker);

        // 如果只有一个申请上庄的且当前没有庄，就重新设置庄
        if (this.applyBankerList.length === 1 && this.curBanker === 0) {
            this.setBanker();
        }
        return cb(null);
    }
    else {
        return cb('coin is not enough');
    }
};

// 申请下庄
// 如果是当前的庄要下庄，需要在空闲或者开牌阶段才能下
// 如果是等待着的要下庄，可以立即下庄
// 先换庄，才广播下庄信息
Room.prototype.cancelBanker = function (msg, cb) {
    var playerId = msg.playerId;
    var index = 0;
    var err = null;
    var cancelBanker = {};
    var needSetBanker = false;
    // 判断是不是当前的庄

    log('玩家%d申请下庄', playerId);
    if (playerId === this.curBanker) {
        // log('庄家是', this.curBanker);
        if (this.checkGameStatus('xiazhu')) {
            err = '下注时不能下庄！';
        }
        else {
            // 如果可以下庄，就需要换庄
            needSetBanker = true;
        }
    }
    else {
        index = this.applyBankerList.indexOf(playerId);
        if (index > -1) {
            this.applyBankerList.splice(index, 1);
        }
        else {
            err = 'no such banker(id: ' + playerId + ') in list';
        }
    }

    // log('err:', err);

    if (err) {
        return cb({ err }, false);
    }
    else {
        // 广播下庄成功消息
        cancelBanker = { bankerId: playerId };
        this.channel.pushMessage('onCancelBanker', cancelBanker, null);

        bankerLog('cancel', cancelBanker);

        // 如果需要换庄，就换庄; 从列表中退出不需要换庄
        // 要下庄成功之后再换庄
        if (needSetBanker) {
            this.setBanker();
        }
        return cb(null, true);
    }
};

// 申请插队上庄
Room.prototype.cutInBanker = function (msg, cb) {
    var playerId = msg.playerId;
    var coin = this.playerManager[playerId].coin;

    var idx = 0;
    var err = null;

    // 既有插队的，插队的又和当前庄家不同 说明上一个插队的还没有上庄，暂时还不能插他前面
    if (this.cutInBanker && this.cutInBanker !== this.curBanker) {
        err = 'already have player cut in';  // 已经有人插队了
    }

    if (!err && coin > self.minBankerCoin + consts.ROOM.CUT_IN_BANKER_COIN) {
        // 如果钱够就先扣钱
        this.playerManager[playerId].coin -= consts.ROOM.CUT_IN_BANKER_COIN;
        // 移动此用户到队头
        idx = this.applyBankerList.indexOf(playerId);
        if (idx > 0) {
            // 如果在队里且不在队头，就先移出队伍
            this.applyBankerList.splice(idx, 1);
        }
        this.applyBankerList.unshift(playerId); // 插入队伍。这种情况下没有上庄也可以插队

        this.cutInBanker = playerId;  // 设置插队用户ID

        // 广播插队成功消息
        var ret = { bankerList: this.applyBankerList };

        this.channel.pushMessage('onCutInBanker', ret, null);

        bankerLog('cutIn', playerId);
    }
    else {
        err = 'money not enough!';
    }

    if (err) {
        return cb({ rel: err });
    }
    else {
        return cb({ rel: 'ok!' });
    }
};

// 换庄类型： 0.从列表中退出，1.主动下庄，2.局数够了下庄，3.钱数不够下庄，4.掉线下庄
Room.prototype.setBanker = function () {
    var setBanker = {};
    var self = this;

    if (this.applyBankerList.length === 0) {
        self.setBankerData(false);
    }
    else {
        // 换庄，初始化局数和分数
        self.setBankerData(true);

        if (this.cutInBanker && parseInt(this.cutInBanker) === this.curBanker) {
            this.cutInBanker = 0;  // 设置插队玩家为庄家之后，就要初始化插队玩家ID记录
        }
    }

    // 广播换庄成功消息
    setBanker = { bankerId: this.curBanker };
    this.channel.pushMessage('onSetBanker', setBanker, null);

    bankerLog('set', setBanker);
    logger('setBanker', setBanker);

    this.bankerOnLine = true;  // 换庄完成之后要恢复在线状态
    // 不管是否有新庄，都要改为在线状态  // 其实应该是bankerOffline, 庄掉线

    return true;
};

// 设置换庄信息
// opt === true : 有新庄 否则无新庄
Room.prototype.setBankerData = function (opt) {
    var playerId = 0;
    var noBanker = true;

    var robotList = this.playerManager.robotList;

    if (opt) {
        // 循环直到找到能上庄的，或者直到申请列表为空
        // 防止出现换到的庄不够上庄金额的情况
        while (noBanker) {
            if (this.applyBankerList.length === 0) {
                this.curBanker = 0;
                noBanker = false;
                break;
            }
            // else {
            playerId = this.applyBankerList.shift();  // 检查此人是否符合金钱要求
            if (this.playerManager.checkApplyBanker(playerId)) {
                this.curBanker = playerId;
                noBanker = false;
            }
            // }
        }
    }
    else {
        this.curBanker = 0;
        // 机器人上庄
        if (robotList.length > 0) {
            // log(robotList);
            this.curBanker = this.getRobotBanker();
            log('设置的机器人庄家是： ', this.curBanker);
        }
        // this.curBanker = 0;
    }
    this.curBankerRound = 0;
    this.curBankerScore = 0;
};

// 从机器人中获取庄家
Room.prototype.getRobotBanker = function () {
    log('从机器人中获取庄家');
    var self = this;
    
    var robotList = this.playerManager.robotList;
    var playerData = this.playerManager.playerData;

    robotList.sort(function () { return Math.random() < 0.5; });
    log('机器人列表：', robotList);

    // log('playerData: ', this.minBankerCoin, playerData);

    for (let i in robotList) {
        let playerId = robotList[i];
        log(playerId);
        if (playerData[playerId].coin > this.minBankerCoin) {
            self.robotBankerRound = utils.genRandomNum(Math.floor(self.maxBankerRound / 3), self.maxBankerRound);
            log('机器人坐庄最大局数是：', self.robotBankerRound);
            return playerId;
        }
    }

    // log('机器人中没有合适的庄家！');
    return 0;
};

// 检查庄的局数
// 检查庄是否在线
Room.prototype.checkBanker = function () {
    log('检查庄家。。。。');

    log('当前庄的局数:', this.getCurBankerRound(), ' 庄：', this.curBanker);

    var self = this;

    var roundCount = this.getCurBankerRound();
    var needKickBanker = false;
    var type;  // 0:轮满局数下庄；1:金钱不足下庄；

    var maxBankerRound = self.maxBankerRound;

    if (roundCount >= maxBankerRound) {
        log('轮满局数下庄！');
        needKickBanker = true;
        type = 0;
    }

    // 或者机器人的随机轮数也到了
    else if (this.playerManager.robotList.indexOf(this.curBanker) > -1 && roundCount >= self.robotBankerRound) {
        log('机器人轮满局数下庄！');
        needKickBanker = true;
        type = 0;
    }

    // 如果有人申请上庄，机器人立即就下庄
    else if (this.applyBankerList.length > 0 && this.playerManager.robotList.indexOf(this.curBanker) > -1) {
        log('真人挤掉机器人！');
        needKickBanker = true;
        type = 2;
    }

    // 如果有庄且庄的金钱少了
    else if (this.curBanker && this.getCurBankerCoin() < self.minBankerCoin) {
        needKickBanker = true;
        type = 1;
    }

    // 上面的情况只要一种。

    if (needKickBanker) {
        // 换庄前要广播
        var cancelBanker = {
            bankerId: this.curBanker,
            type
        };

        this.channel.pushMessage('onCancelBanker', cancelBanker, null);

        // bankerLog('cancel', cancelBanker);

        this.setBanker();
        return;  // 后面的不再执行
    }

    // 如果结算时这个庄家掉线了
    if (!this.bankerOnLine) {
        log('庄家掉线了！');
        this.setBanker();
        // 下一步就会检查是否有掉线玩家，不用在这里踢掉
        return;
    }

    // 如果没有庄的话    
    if (!this.curBanker) {
        log('没有庄！');
        // 如果没有人上庄的话，就会上一个机器人
        this.setBanker();
        return;
    }
};

// 检查掉线玩家
Room.prototype.checkOfflinePlayer = function () {
    var offlinePlayer = this.offlinePlayer;
    var len = offlinePlayer.length;
    var players = this.playerManager.playerData;

    log('检查掉线玩家', offlinePlayer);

    if (len <= 0) {
        log('无掉线玩家!');
        return;
    }

    for (var i = 0; i < len; i++) {
        this.playerManager.kickPlayer({ playerId: offlinePlayer[i] });
    }

    // 全都踢出之后，掉线玩家清空
    offlinePlayer = [];
    log('初始化掉线玩家列表', offlinePlayer);
};

// 是否有庄
Room.prototype.existsBanker = function () {
    return this.getCurBanker() > 0;
};

// 获取到庄的id
Room.prototype.getCurBanker = function () {
    return this.curBanker;
};

// 获取到庄的金钱
Room.prototype.getCurBankerCoin = function () {
    if (this.curBanker) {
        return this.playerManager.playerData[this.curBanker].coin;
    }
    else {
        return 0;
    }
};

// 获取到庄的局数
Room.prototype.getCurBankerRound = function () {
    return this.curBankerRound;
};

// 获取庄的分数
Room.prototype.getCurBankerScore = function () {
    return this.curBankerScore;
};

// 获取到庄的信息
Room.prototype.getCurBankerData = function () {
    return {
        bankerId: this.getCurBanker(),
        bankerRound: this.getCurBankerRound(),
        bankerScore: this.getCurBankerScore()
    };
};

// 获取到庄的列表
Room.prototype.getBankerList = function () {
    return this.applyBankerList || [];
};

// 检查机器人数量是否合适，并进行相应的处理
// 还要检查机器人的金钱及局数是否合适
Room.prototype.checkRobot = function () {
    log('检查房间中的机器人 >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>');
    var self = this;

    var playerManager = this.playerManager;
    var robotList = playerManager.robotList;
    var robotNum = playerManager.robotNum;
    var playerNum = playerManager.playerNum;
    var playerData = playerManager.playerData;

    // 暂时添加2个机器人    
    // self.setRobotNum = 0;

    if (self.setRobotNum > robotNum) {
        // 设定数量多则添加机器人
        // 暂时每次最多添加20个机器人
        playerManager.addRobot2PlayerData(utils.genRandomNum(0, 3));
    }
    else if (self.setRobotNum < robotNum) {
        // 设定数量少则删除机器人
        playerManager.kickRobotFromPlayerData(utils.genRandomNum(0, 3));
    }
    else {
        log('机器人数量 === 设置数量！');
    }

    // 删除机器人
    var coin;
    var count = 0;
    var robotSet = playerManager.robotSet;

    // 空闲时间才因为金钱不符合要求换机器人
    if (this.checkGameStatus('free')) {
        log('检查机器人金钱！');
        for (var playerId of robotList) {
            coin = playerData[playerId].coin;
            if (coin > robotSet.maxCoin || coin < robotSet.minCoin) {
                playerManager.kickRobotById(playerId);
                count++;
            }
        }
    }

    log('玩家总数量： %s, 机器人总数量： %s', playerNum, robotNum);
};

/**
 *检查游戏状态
 */
Room.prototype.checkGameStatus = function (type) {
    var gameStatus = this.getGameStatus().gameStatus;
    var constConf = consts.BaiGameStatus;

    switch (type) {
        case 'free': return gameStatus === constConf.game_status_free;
        case 'xiazhu': return gameStatus === constConf.game_status_xiazhu;
        case 'kaipai': return gameStatus === constConf.game_status_kaipai;
        default: return 0;
    }
};

Room.prototype.log = function () {
    console.log.apply(null, arguments);
};
Room.prototype.debug = function () {
    console.debug.apply(null, arguments);
};
Room.prototype.warn = function () {
    console.warn.apply(null, arguments);
};

// /////////////////////////////////////////////////////
/**
 * Expose 'Room' constructor.
 */
module.exports = Room;

